import { Menu, IMenu } from '../models/menu'
import {
  DatabaseError,
  NotFoundError,
  ValidationError
} from '../errors/CustomError'
import { isMongoError } from '../errors/MongoError'
import { listToTree } from '../utils/utils'

class MenuService {
  // 创建菜单
  async create(menuData: Partial<IMenu>): Promise<IMenu> {
    try {
      // 设置默认层级为1（顶级菜单）
      if (!menuData.level) {
        menuData.level = 1
      }

      const menu = new Menu(menuData)
      return await menu.save()
    } catch (error) {
      if (isMongoError(error) && error.code === 11000) {
        const mongoError = error as any
        if (mongoError.keyValue?.name) {
          throw new ValidationError('菜单名称已存在')
        }
        if (mongoError.keyValue?.code) {
          throw new ValidationError('菜单编码已存在')
        }
        throw new ValidationError('菜单数据重复')
      }
      if (error instanceof Error && error.name === 'ValidationError') {
        throw new ValidationError('菜单数据验证失败', (error as any).errors)
      }

      throw new DatabaseError('创建菜单失败')
    }
  }

  // 删除菜单
  async delete(id: number): Promise<boolean> {
    try {
      const result = await Menu.findOneAndDelete({ id })

      if (!result) {
        throw new NotFoundError('菜单未找到')
      }

      return true
    } catch (error) {
      if (error instanceof NotFoundError) {
        throw error
      }
      throw new DatabaseError('删除菜单失败')
    }
  }

  // 删除菜单及其所有子菜单
  async deleteWithChildren(id: number): Promise<boolean> {
    try {
      // 查找要删除的菜单
      const menu = await Menu.findOne({ id })

      if (!menu) {
        throw new NotFoundError('菜单未找到')
      }

      // 递归查找所有子菜单ID
      const allIdsToDelete = await this.getAllChildMenuIds(id)
      allIdsToDelete.push(id) // 添加当前菜单ID

      // 批量删除所有相关菜单
      await Menu.deleteMany({ id: { $in: allIdsToDelete } })

      return true
    } catch (error) {
      if (error instanceof NotFoundError) {
        throw error
      }
      throw new DatabaseError('删除菜单失败')
    }
  }

  // 更新菜单
  async update(id: number, updateData: Partial<IMenu>): Promise<IMenu | null> {
    try {
      // 移除不能更新的字段
      const { id: menuId, created_at, ...updateFields } = updateData

      const menu = await Menu.findOneAndUpdate({ id }, updateFields, {
        new: true
      })

      if (!menu) {
        throw new NotFoundError('菜单未找到')
      }

      return menu
    } catch (error) {
      if (error instanceof NotFoundError || error instanceof ValidationError) {
        throw error
      }
      if (isMongoError(error) && error.code === 11000) {
        const mongoError = error as any
        if (mongoError.keyValue?.name) {
          throw new ValidationError('菜单名称已存在')
        }
        if (mongoError.keyValue?.code) {
          throw new ValidationError('菜单编码已存在')
        }
        throw new ValidationError('菜单数据重复')
      }
      if (error instanceof Error && error.name === 'ValidationError') {
        throw new ValidationError('菜单数据验证失败', (error as any).errors)
      }
      throw new DatabaseError('更新菜单失败')
    }
  }

  // 查询所有菜单
  async findAll(query: any): Promise<IMenu[] | any[]> {
    try {
      const type = query.type?.toString() || ''

      let menus: IMenu[] = []

      if (query.name) {
        // 先找出所有匹配的菜单
        const matchedMenus = await Menu.find({
          name: { $regex: query.name, $options: 'i' }
        })

        // 获取所有匹配菜单的ID
        const matchedIds = matchedMenus.map((menu) => menu.id)

        // 对于每个匹配的菜单，获取其所有父级菜单ID
        const allParentIds: number[] = []
        for (const menu of matchedMenus) {
          const parentIds = await this.getParentMenuIds(menu.parent_id)
          allParentIds.push(...parentIds)
        }

        // 同时获取所有匹配菜单的子菜单ID
        const allChildIds: number[] = []
        for (const id of matchedIds) {
          const childIds = await this.getAllChildMenuIds(id)
          allChildIds.push(...childIds)
        }

        // 合并所有需要的ID并去重
        const allNeededIds = [
          ...new Set([...matchedIds, ...allParentIds, ...allChildIds])
        ]

        // 查询所有相关的菜单
        menus = await Menu.find({ id: { $in: allNeededIds } })
      } else {
        // 如果没有搜索条件，获取所有菜单
        menus = await Menu.find({})
      }

      const data = menus.map((menu) => menu.toJSON())

      if (type === 'tree') {
        return listToTree(data, 'parent_id', 'id')
      }
      return data
    } catch (error) {
      throw new DatabaseError('查询菜单列表失败')
    }
  }

  // 查询单个菜单
  async findOne(id: number): Promise<IMenu | null> {
    try {
      const menu = await Menu.findOne({ id })

      if (!menu) {
        throw new NotFoundError('菜单未找到')
      }

      return menu
    } catch (error) {
      if (error instanceof NotFoundError) {
        throw error
      }
      throw new DatabaseError('查询菜单失败')
    }
  }

  // 根据父菜单ID查询菜单及所有子菜单（返回树形结构）
  async findByParentId(parentId: number | null): Promise<any[]> {
    try {
      // 获取父菜单及所有子菜单
      let menus: IMenu[] = []

      if (parentId !== null) {
        // 获取指定的父菜单
        const parentMenu = await Menu.findOne({ id: parentId })
        if (parentMenu) {
          const json = parentMenu.toJSON() as unknown as IMenu
          menus.push(json)
        }

        // 获取所有子菜单
        const allChildIds = await this.getAllChildMenuIds(parentId)
        const childMenus = await Menu.find({ id: { $in: allChildIds } })
        menus = menus.concat(childMenus.map((menu) => {
          return menu.toJSON() as unknown as IMenu
        }))
      } else {
        // 如果parentId为null，获取所有菜单
        const allMenus = await Menu.find({})
        menus = allMenus.map((menu) => {
          return menu.toJSON() as unknown as IMenu
        })
      }

      // 转换为树形结构
      return listToTree(menus, 'parent_id', 'id')
    } catch (error) {
      throw new DatabaseError('查询菜单树形结构失败')
    }
  }

    // 获取所有父级菜单ID
  private async getParentMenuIds(parentId: number | null): Promise<number[]> {
    const parentIds: number[] = []
    let currentParentId = parentId

    while (currentParentId !== null) {
      const parentMenu = await Menu.findOne({ id: currentParentId })
      if (parentMenu) {
        parentIds.push(parentMenu.id)
        currentParentId = parentMenu.parent_id
      } else {
        break
      }
    }

    return parentIds
  }

  // 获取所有子菜单ID
  private async getAllChildMenuIds(parentId: number): Promise<number[]> {
    try {
      // 查找直接子菜单
      const directChildren = await Menu.find({ parent_id: parentId })
      let allChildIds: number[] = directChildren.map((child) => child.id)

      // 递归查找子菜单的子菜单
      for (const child of directChildren) {
        const grandChildIds = await this.getAllChildMenuIds(child.id)
        allChildIds = allChildIds.concat(grandChildIds)
      }

      return allChildIds
    } catch (error) {
      throw new DatabaseError('查询子菜单失败')
    }
  }
}

export default new MenuService()
